﻿using JmShared;
using SuperSocket.SocketBase;
using SuperWebSocket.SubProtocol;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WebSocketServer.Filters;
using WebSocketServer.models;
using WebSocketServer.Models;
using WebSocketServer.Utilities;

namespace WebSocketServer.Commands
{
    [LoggedInValidationFilter(Order = 0)]
    [LogTimeCommandFilter(Order = 1)]
    public class Message : JsonSubCommand<JiveClientSession, ChatMessage>
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        protected override void ExecuteJsonCommand(JiveClientSession session, ChatMessage msg)
        {
            try
            {
                Debug.Print("Received message of type {0}", msg.MessageType);
                ChatClient senderClient = session.ChatClient;

                if (msg == null) throw new ArgumentNullException("msg", "message is null");

                msg.MessageRelayTime = DateTime.UtcNow;

                switch (msg.MessageType)
                {
                    case MessageType.Logout:
                        {

                            //  client.UpdateDeviceSync(client.LastMessageSyncTime);
                            session.Close(SuperSocket.SocketBase.CloseReason.ClientClosing);

                            break;
                        }
                    case MessageType.GroupMessage:
                        {
                            UserInfo sender = Clients.ConnectedClients[session].User;
                            senderClient.LastMessageSyncTime = msg.MessageRelayTime;
                            if (msg.MessageID.HasValue)
                                session.SendReceipt(new ChatReceipt(msg.MessageType, msg.MessageID.Value));
                            AddMessageToQueue(msg);
                            session.AppServer.SendToGroup(msg, msg.RecipientID, session, true);
                            //   senderClient.UpdateDeviceSync();

                            break;
                        }
                    case MessageType.IsWriting:
                    case MessageType.Nudge:
                        {
                            senderClient.LastMessageSyncTime = msg.MessageRelayTime;
                            if (msg.Message == "PM")
                            {
                                var recipients = Clients.GetChatClientsFromUsername(msg.RecipientID);
                                if (recipients.Any(cli => cli.FriendsList.Any(f => f.Username == senderClient.User.UserName && f.IsIgnored)))
                                    return;
                                foreach (ChatClient cli in recipients)
                                {
                                    JiveClientSession sendTo = cli.CurrentSession;
                                    if (sendTo != null && sendTo.Connected)
                                        sendTo.SendMessage(msg);
                                }
                            }
                            else
                            {
                                session.AppServer.SendToGroup(msg, msg.RecipientID, session, true);
                            }
                            break;
                        }
                    case MessageType.PrivateMessage:
                        {
                            if (msg.MessageID.HasValue)
                                session.SendReceipt(new ChatReceipt(msg.MessageType, msg.MessageID.Value));
                            senderClient.LastMessageSyncTime = msg.MessageRelayTime;
                            var recipients = Clients.GetChatClientsFromUsername(msg.RecipientID);
                            if (recipients.Any(cli => cli.FriendsList.Any(f => f.Username == senderClient.User.UserName && f.IsIgnored)))
                                return;

                            if (msg.SenderUsername == msg.RecipientID)
                                recipients = recipients.Where(x => x.DeviceID != msg.SenderDeviceID);


                            AddMessageToQueue(msg);

                            foreach (ChatClient receiverClient in recipients)
                            {
                                JiveClientSession receiverConnection = receiverClient.CurrentSession;
                                if (receiverConnection.SendMessage(msg))
                                {
                                    receiverConnection.ChatClient.LastMessageSyncTime = msg.MessageRelayTime;
                                    receiverConnection.ChatClient.UpdateDeviceSync();
                                    receiverConnection.AppServer.TrySendToastMessage(senderClient, receiverClient, msg);
                                }
                                if (session.AppServer.ValidToastUnreadMessage(receiverClient, msg))
                                {
                                    receiverClient.ReceivedMessageCount++;
                                    if (!receiverClient.UnreadMessageStats.ContainsKey(senderClient.User.UserName))
                                        receiverClient.UnreadMessageStats[senderClient.User.UserName] = new UnreadUserMessages()
                                        {
                                            LastReceivedMessageTime = msg.MessageRelayTime.Value,
                                            UnreadMessageCount = 1,
                                            SenderUsername = senderClient.User.UserName,
                                            SenderNickname = senderClient.User.NickName,
                                        };
                                    else
                                    {
                                        receiverClient.UnreadMessageStats[senderClient.User.UserName].UnreadMessageCount++;
                                    }
                                }
                            }
                            var offlineClientsForUser = DB.DbManager.ReadOfflineWPToastPushClientsForUser(msg.RecipientID);
                            foreach (OfflineClient offlineClient in offlineClientsForUser)
                            {
                                session.AppServer.TrySendToastMessage(senderClient, offlineClient, msg);
                            }

                            var senderTargets = Clients.GetChatClientsFromUsername(senderClient.User.UserName);
                            foreach (ChatClient cli in senderTargets.Where(x => x.UserID != senderClient.UserID && x.Username != msg.RecipientID))
                            {
                                JiveClientSession targetSession = cli.CurrentSession;

                                msg.SenderDeviceID = cli.DeviceID;
                                msg.SenderUsername = cli.Username;
                                targetSession.SendMessage(msg);
                            }
                            //   senderClient.UpdateDeviceSync();
                            break;
                        }
                    case MessageType.SendPushUri:
                        {
                            senderClient.PushUri = msg.Message;
                            DB.DbManager.StorePushUri(senderClient.User.ID, senderClient.MemberID, senderClient.PushUri);
                            break;
                        }
                    case MessageType.ResetUnreadMessageCount:
                        {
                            senderClient.ReceivedMessageCount = 0;
                            senderClient.DeliveredMessageCount = 0;
                            break;
                        }
                    case MessageType.SendFriendRequest:
                        {
                            var result = DB.DbManager.StoreFriendRequest(senderClient.MemberID, msg.RecipientID, msg.Message);
                            SendJsonMessage(session, new DatabaseQueryResponse(result.Success, result.StatusText));
                            if (result.Success == true)
                            {
                                foreach (ChatClient cli in Clients.GetChatClientsFromUsername(msg.RecipientID))
                                {
                                    cli.Notifications.Add(new FriendInviteNotification()
                                    {
                                        IsRead = false,
                                        NotificationText = msg.Message,
                                        SenderUsername = senderClient.User.UserName
                                    });
                                    cli.UpdateNotifications(false);
                                }
                            }
                            break;
                        }
                    case MessageType.AcceptFriendRequest:
                        {
                            var result = DB.DbManager.AcceptFriendInvite(senderClient.MemberID, msg.RecipientID);
                            if (result.Success)
                            {
                                senderClient.CurrentSession.SendClientList(true);

                                var friendClients = Clients.GetChatClientsFromUsername(msg.RecipientID);
                                foreach (ChatClient friendCli in friendClients)
                                {
                                    friendCli.CurrentSession.SendClientList(true);
                                }
                                senderClient.UpdateNotifications(true);
                            }
                            break;
                        }
                    case MessageType.DeclineFriendRequest:
                        {
                            DB.DbManager.DeclineFriendInvite(senderClient.MemberID, msg.RecipientID);
                            senderClient.UpdateNotifications(true);
                            break;
                        }
                    case MessageType.IgnoreContact:
                        {
                            DB.DbManager.IgnoreContact(senderClient.MemberID, msg.RecipientID);
                            var contact = senderClient.FriendsList.FirstOrDefault(x => x.Username == msg.RecipientID);
                            if (contact != null)
                            {
                                contact.IsIgnored = true;
                                if (senderClient.UserSettings.IgnoredContacts == null)
                                    senderClient.UserSettings.IgnoredContacts = new List<string>();
                                if (!senderClient.UserSettings.IgnoredContacts.Contains(contact.Username))
                                    senderClient.UserSettings.IgnoredContacts.Add(contact.Username);
                                foreach (ChatClient cli in Clients.GetChatClientsFromUsername(contact.Username))
                                {
                                    cli.CurrentSession.SendClientList(false);
                                }
                            }
                            break;
                        }
                    case MessageType.UnignoreContact:
                        {
                            DB.DbManager.UnignoreContact(senderClient.MemberID, msg.RecipientID);

                            var contact = senderClient.FriendsList.FirstOrDefault(x => x.Username == msg.RecipientID);
                            if (contact != null)
                            {
                                contact.IsIgnored = false;
                                senderClient.UserSettings.IgnoredContacts.Remove(contact.Username);
                                foreach (ChatClient cli in Clients.GetChatClientsFromUsername(contact.Username))
                                {
                                    cli.CurrentSession.SendClientList(false);
                                }
                            }
                            break;
                        }
                    case MessageType.SendGroupInvite:
                        {
                            ChatGroup grp = StaticMethods.StringToObject<ChatGroup>(msg.Message);
                            var result = DB.DbManager.StoreGroupChatInvite(senderClient.MemberID, msg.RecipientID, grp.ID);
                            SendJsonMessage(session, new DatabaseQueryResponse(result.Success, result.StatusText));
                            if (result.Success)
                            {
                                foreach (ChatClient cli in Clients.GetChatClientsFromUsername(msg.RecipientID))
                                {
                                    cli.Notifications.Add(new GroupInviteNotification()
                                    {
                                        IsRead = false,
                                        NotificationText = string.Empty,
                                        SenderUsername = senderClient.User.UserName,
                                        Group = grp
                                    });
                                    cli.UpdateNotifications(false);
                                }
                            }
                            break;
                        }
                    case MessageType.CreateGroup:
                        {
                            var result = DB.DbManager.CreateChatGroup(msg.Message, senderClient.MemberID);
                            SendJsonMessage(session, new DatabaseQueryResponse(result.Success, result.StatusText));
                            if (result.Success == true)
                            {
                                senderClient.BroadcastChatGroups(true, MessageReceipient.ContactsAndClient);
                            }
                            break;
                        }
                    case MessageType.DeleteGroup:
                        {
                            var result = DB.DbManager.DeleteChatGroup(msg.Message, senderClient.MemberID);
                            if (result.Success == true)
                            {
                                senderClient.UpdateNotifications(true);
                                ChatGroup grp = senderClient.User.ChatGroups.FirstOrDefault(x => x.ID == msg.Message);
                                List<ChatClient> clientsToUpdate = new List<ChatClient>();
                                foreach (KeyValuePair<string, GroupPrivilege> kvp in grp.GroupMembers)
                                {
                                    var clients = Clients.GetChatClientsFromUsername(kvp.Key);
                                    foreach (ChatClient cli in clients)
                                    {
                                        cli.SetUsersChatGroups();
                                        clientsToUpdate.Add(cli);
                                    }
                                }
                                foreach (ChatClient cli in clientsToUpdate)
                                {
                                    cli.CurrentSession.SendClientList(true);
                                }
                                foreach (Friend fr in senderClient.FriendsList)
                                {
                                    var clients = Clients.GetChatClientsFromUsername(fr.Username);
                                    foreach (ChatClient cli in clients)
                                    {
                                        if (cli.Notifications.Any(x => x.NotificationType == NotificationType.GroupInvite && ((GroupInviteNotification)x).Group.ID == msg.Message))
                                        {
                                            cli.UpdateNotifications(true);
                                        }
                                    }
                                }
                            }
                            break;
                        }
                    case MessageType.AcceptGroupInvite:
                        {
                            var result = DB.DbManager.AcceptChatGroupInvite(senderClient.MemberID, Convert.ToInt32(msg.RecipientID));
                            if (result.Success)
                            {
                                senderClient.BroadcastChatGroups(true, MessageReceipient.ContactsAndClient);

                                senderClient.UpdateNotifications(true);
                                ChatGroup grp = senderClient.User.ChatGroups.FirstOrDefault(x => x.ID == msg.RecipientID);
                                List<ChatClient> clientsToUpdate = new List<ChatClient>();
                                foreach (KeyValuePair<string, GroupPrivilege> kvp in grp.GroupMembers)
                                {
                                    var clients = Clients.GetChatClientsFromUsername(kvp.Key);
                                    foreach (ChatClient cli in clients)
                                    {
                                        if (cli.MemberID != senderClient.MemberID)
                                            cli.SetUsersChatGroups();
                                        clientsToUpdate.Add(cli);
                                    }
                                }
                                foreach (ChatClient cli in clientsToUpdate)
                                {
                                    cli.CurrentSession.SendClientList(true);
                                }


                            }
                            break;
                        }
                    case MessageType.DeclineGroupInvite:
                        {
                            DB.DbManager.DeclineChatGroupInvite(senderClient.MemberID, Convert.ToInt32(msg.RecipientID));
                            senderClient.UpdateNotifications(true);
                            break;
                        }
                    case MessageType.DismissServerMessageNotification:
                        DB.DbManager.DismissServerInfoNotification(senderClient.MemberID, Convert.ToInt32(msg.RecipientID));
                        senderClient.UpdateNotifications(true);
                        break;
                    case MessageType.BlockGroup:
                        {
                            DB.DbManager.BlockGroup(senderClient.MemberID, Convert.ToInt32(msg.RecipientID));
                            if (senderClient.UserSettings.BlockedGroups == null)
                                senderClient.UserSettings.BlockedGroups = new List<string>();
                            senderClient.UserSettings.BlockedGroups.Add(msg.RecipientID);
                            break;
                        }
                    case MessageType.UnblockGroup:
                        {
                            DB.DbManager.UnblockGroup(senderClient.MemberID, Convert.ToInt32(msg.RecipientID));
                            if (senderClient.UserSettings.BlockedGroups == null)
                                senderClient.UserSettings.BlockedGroups = new List<string>();
                            senderClient.UserSettings.BlockedGroups.Remove(msg.RecipientID);
                            break;
                        }
                    case MessageType.LeaveGroup:
                        {
                            var result = DB.DbManager.LeaveGroup(senderClient.MemberID, Convert.ToInt32(msg.RecipientID));
                            if (result.Success)
                            {
                                ChatGroup grp = senderClient.User.ChatGroups.FirstOrDefault(x => x.ID == msg.RecipientID);
                                List<ChatClient> clientsToUpdate = new List<ChatClient>();
                                foreach (KeyValuePair<string, GroupPrivilege> kvp in grp.GroupMembers)
                                {
                                    var clients = Clients.GetChatClientsFromUsername(kvp.Key);
                                    foreach (ChatClient cli in clients)
                                    {
                                        if (cli.MemberID == senderClient.MemberID)
                                            cli.BroadcastChatGroups(true, MessageReceipient.Client);
                                        else
                                            cli.SetUsersChatGroups();

                                        clientsToUpdate.Add(cli);
                                    }
                                }
                                foreach (ChatClient cli in clientsToUpdate)
                                {
                                    cli.CurrentSession.SendClientList(true);
                                }
                            }
                            break;
                        }
                    case MessageType.RenameGroup:
                        {
                            var result = DB.DbManager.RenameGroup(senderClient.MemberID, Convert.ToInt32(msg.RecipientID), msg.Message);
                            SendJsonMessage(session, new DatabaseQueryResponse(result.Success, result.StatusText));
                            if (result.Success == true)
                            {
                                senderClient.BroadcastChatGroups(true, MessageReceipient.ContactsAndClient);
                            }
                            break;
                        }
                    case MessageType.RemoveMemberFromGroup:
                        {
                            var result = DB.DbManager.RemoveMemberFromGroup(senderClient.MemberID, Convert.ToInt32(msg.Message), msg.RecipientID);
                            SendJsonMessage(session, new DatabaseQueryResponse(result.Success, result.StatusText));

                            if (result.Success)
                            {
                                ChatGroup grp = senderClient.User.ChatGroups.FirstOrDefault(x => x.ID == msg.Message);
                                List<ChatClient> clientsToUpdate = new List<ChatClient>();
                                foreach (KeyValuePair<string, GroupPrivilege> kvp in grp.GroupMembers)
                                {
                                    var clients = Clients.GetChatClientsFromUsername(kvp.Key);
                                    foreach (ChatClient cli in clients)
                                    {
                                        if (cli.User.UserName == msg.RecipientID)
                                            cli.BroadcastChatGroups(true, MessageReceipient.Client);
                                        else
                                            cli.SetUsersChatGroups();
                                    }
                                }
                                foreach (ChatClient cli in clientsToUpdate)
                                {
                                    cli.CurrentSession.SendClientList(true);
                                }
                            }
                            break;
                        }
                    //case MessageType.PrepareLogout:
                    //    {
                    //        session.PrepareLogout = true;
                    //        break;
                    //    }
                    case MessageType.ClientReady:
                        {
                            session.IsReadyToReceiveMessages = true;
                            senderClient.RequestMissingEmoticons();
                            senderClient.ProcessQueue();


                            //Broadcast authenticated status to other connected clients.
                            session.AppServer.SendToAll(new ServerInfoMessage() { MessageType = ServerInfoMessageType.ContactAuthenticated, UserInfo = senderClient.User }, session, false, true);
                            break;
                        }
                }
            }
            catch (Exception err)
            {
                log.Error("Exception in Message command", err);
                session.Close(CloseReason.ApplicationError);
            }
        }
        private void AddMessageToQueue(ChatMessage msg)
        {
            switch (msg.MessageType)
            {
                case MessageType.PrivateMessage:
                    DB.DbManager.StorePrivateMessage(msg);
                    break;
                case MessageType.GroupMessage:
                    DB.DbManager.StoreGroupMessage(msg);
                    break;
                default:
                    break;
            }
        }
    }
}
